/** cesium 基本库 */
import * as Cesium from 'cesium';
/** 封装的坐标转换方法集合 */
import * as CesiumCoord from './common/CesiumCrood';
/** 封装的分组实体集合，用于管理实体 */
import GroupEntityCollection from '../../Common/GroupEntityCollection';
/** 封装的提示文字悬浮框 */
import TipDOM from './common/TipDOM';
/** 生成uuid */
import { v4 } from 'uuid';

/**
 * 绘制-点的方式
 *
 *
 * 支持一下实例方法
 * startDrawPromise：promise 绘制方法
 * endDrawPromise： promise 中断
 * startDraw: 开始绘制
 * endDraw: 结束绘制
 * removeDraw: 清除当前绘制
 * removeAllDraw: 清除所有绘制
 * getDraw： 获取当前的绘制的 entity
 * getAllDraw: 获取当前所有的enity
 */
export default class DrawPoint {
    /** cesium viewer */
    viewer = null;
    /** 提示dom实体操作 */
    tip = null;
    /** 当次绘制的id,用于管理实体 */
    groupId = '';
    /** 存储当次绘制的点位集合  */
    positionList = [];
    /** 事件对象  */
    handler = null;
    /** 实体集合：配合groupId 用于实体操作 */
    entityCollection = null;
    /** 绘制状态：true 正在绘制， false 结束  */
    drawState = false;
    /** 绘制配置项 */
    drawConfig = {
        measure: false, // 可选， 是否测量
        createPointEntity: null, //可选， (世界坐标)=> PointEntity
        createLabelEntity: null, //可选， (世界坐标)=> LabelEntity
        promiseDrawStopResolve: true, // 可选，在调用 startDrawPromise 的情况下，中断Promise 是以 resolve 还是 reject 的方式中断，默认 resolve 方式中断
    };
    /** 全局配置项：  全局配置涵盖默认配置，可在初始化之前给定，但是绘制函数时，可绘制配置优先级更高*/
    globalConfig = {
        /** 绘制配置 */
        measure: false, // 可选， 是否测量
        createPointEntity: null, //可选， (世界坐标)=> PointEntity
        createLabelEntity: null, //可选， (世界坐标)=> LabelEntity
        promiseDrawStopResolve: true, // 可选，在调用 startDrawPromise 的情况下，中断Promise 是以 resolve 还是 reject 的方式中断，默认 resolve 方式中断

        /** 私有全局配置 */
        tipDomId: '', // 提示的DOM,
        onStateChange: null, // 监听绘制状态 （当前绘制状态）=>
    };
    constructor(viewer, globalConfig) {
        this.viewer = viewer;
        /** 设置全局配置 */
        this.globalConfig = {
            ...this.globalConfig,
            ...globalConfig,
        };
    }
    /** 改变绘制状态 */
    changeDrawState(newValue) {
        if (this.drawState !== newValue) {
            this.drawState = newValue;
            if (this.globalConfig.onStateChange) {
                this.globalConfig.onStateChange(newValue);
            }
        }
    }
    /** 返回当次绘制结果 */
    resolveCurrentDrawResult() {
        const entity = this.entityCollection.getEntitiesByGroupId(this.groupId);
        return {
            entity,
            positionList: JSON.parse(JSON.stringify(this.positionList)),
        };
    }
    /** 绘制前初始化 */
    initDraw(config) {
        /** 绘制时注册 */
        if (!this.handler) {
            this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
        }
        if (!this.entityCollection) {
            this.entityCollection = new GroupEntityCollection(this.viewer); // 集合
        }
        if (!this.tip) {
            this.tip = new TipDOM(this.viewer, this.globalConfig.tipDomId);
        }

        /** 设置绘制配置项 */
        /** 恢复到全局配置 */
        this.drawConfig = {
            measure: this.globalConfig.measure, // 可选， 是否测量
            createPointEntity: this.globalConfig.createPointEntity, //可选， (世界坐标)=> PointEntity
            createLabelEntity: this.globalConfig.createLabelEntity, //可选， (世界坐标)=> LabelEntity
            promiseDrawStopResolve: this.globalConfig.promiseDrawStopResolve, // 可选，在调用 startDrawPromise 的情况下，中断Promise 是以 resolve 还是 reject 的方式中断，默认 resolve 方式中断
        };
        /** 当前绘制 */
        if (config) {
            this.drawConfig = {
                ...this.drawConfig,
                ...config,
            };
        }

        /** 开启dom提示 */
        this.tip.show('单击开始绘制');
        /** 重置绘制变量 */
        this.positionList = [];
        this.groupId = v4(); // 每一次绘制就是一次新的记录
        this.changeDrawState(true);
    }
}

/**
 * promise:开始绘制
 * @param {配置项} config
 * @returns
 */
DrawPoint.prototype.startDrawPromise = function (config) {
    let timer;
    const IntervalTime = 300; // 中断promise 轮询时间
    return new Promise((resolve, reject) => {
        try {
            this.initDraw(config); // 初始化变量
            /** 开启一个定时器，不断轮询 drawState.value 状态，如果 drawState变为false 则关闭这个promise */
            timer = setInterval(() => {
                if (!this.drawState) {
                    timer && window.clearInterval(timer);
                    handlerRightClick.call(this);
                    /** 中断promise 以什么时间中断 */
                    if (this.drawConfig.promiseDrawStopResolve) {
                        const res = this.resolveCurrentDrawResult();
                        resolve(res);
                    } else {
                        reject('手动中断,且以reject 方式中断');
                    }
                }
            }, IntervalTime);
            /** 左键单击事件 */
            this.handler.setInputAction(
                handlerLeftClick.bind(this),
                Cesium.ScreenSpaceEventType.LEFT_CLICK
            );
            /** 鼠标移动事件 */
            this.handler.setInputAction(
                handlerMouseMove.bind(this),
                Cesium.ScreenSpaceEventType.MOUSE_MOVE
            );
            /** 右键单击事件 */
            this.handler.setInputAction(() => {
                handlerRightClick.call(this);
                const res = this.resolveCurrentDrawResult();
                /** 中断轮询器 */
                timer && window.clearInterval(timer);
                resolve(res);
            }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
        } catch (e) {
            handlerRightClick.call(this);
            /** 中断轮询器 */
            timer && window.clearInterval(timer);
            console.error(e);
            reject(e);
        }
    });
};

/**
 * peomise:结束绘制
 */
DrawPoint.prototype.endDrawPromise = function () {
    return new Promise(resolve => {
        if (this.drawState) this.changeDrawState(false);
        const res = this.resolveCurrentDrawResult();
        resolve(res);
    });
};

/**
 * 开始绘制点
 */
DrawPoint.prototype.startDraw = function (config) {
    /** 绘制前初始化 */
    this.initDraw(config);

    /** 左键单击事件 */
    this.handler.setInputAction(
        handlerLeftClick.bind(this),
        Cesium.ScreenSpaceEventType.LEFT_CLICK
    );
    /** 鼠标移动事件 */
    this.handler.setInputAction(
        handlerMouseMove.bind(this),
        Cesium.ScreenSpaceEventType.MOUSE_MOVE
    );
    /** 右键单击事件 */
    this.handler.setInputAction(
        handlerRightClick.bind(this),
        Cesium.ScreenSpaceEventType.RIGHT_CLICK
    );
};

/**
 * 结束绘制点
 */
DrawPoint.prototype.endDraw = function () {
    handlerRightClick.call(this);
};

/**
 * 删除当次绘制的所有实体
 */
DrawPoint.prototype.removeDraw = function () {
    this.entityCollection?.remove(this.groupId);
};
/**
 * 删除绘制所有点
 */
DrawPoint.prototype.removeAllDraw = function () {
    this.entityCollection?.removeAll(); // 清空集合所有
};

/**
 * 获取当次绘制所有实体
 * @returns
 */
DrawPoint.prototype.getDraw = function () {
    return this.entityCollection?.getEntitiesByGroupId(this.groupId) || [];
};

/**
 * 获取所有绘制实体
 * @returns
 */
DrawPoint.prototype.getAllDraw = function () {
    return this.entityCollection?.getAllEntities() || [];
};

/** 左键点击事件 */
function handlerLeftClick(event) {
    const viewer = this.viewer;
    const { measure, createLabelEntity, createPointEntity } = this.drawConfig;
    const cartesian = CesiumCoord.ScreenToCartesian3(viewer, event.position); // 屏幕坐标转世界坐标

    if (!Cesium.defined(cartesian)) return;
    this.positionList.push(cartesian.clone());
    // 标注实体
    if (measure) {
        const labelEntity = createLabelEntity
            ? createLabelEntity(cartesian)
            : defaultCreateLabelEntity(cartesian);

        this.entityCollection.add(labelEntity, this.groupId); // 加入集合
    }
    // 点实体
    const pointEntity = createPointEntity
        ? createPointEntity(cartesian)
        : defaultCreatePointEntity(cartesian);
    this.entityCollection.add(pointEntity, this.groupId); // 加入集合
}

/** 鼠标移动事件 */
function handlerMouseMove(event) {
    const { x, y } = event.endPosition;
    this.tip.moveTipDOMPosition(x, y); // 改变 TipDOM 位置
}

/** 右键点击事件 */
function handlerRightClick() {
    this.tip?.close();
    // 移除绑定的事件
    this.handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
    this.handler?.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
    this.handler?.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    this.changeDrawState(false);
}

/**
 * 创建点
 * @param {* 世界坐标} cartesian
 * @returns
 */
function defaultCreatePointEntity(cartesian) {
    const entity = new Cesium.Entity({
        position: cartesian, // 世界坐标
        point: {
            color: Cesium.Color.YELLOW,
            pixelSize: 15,
            disableDepthTestDistance: Number.POSITIVE_INFINITY,
        },
    });
    return entity;
}

/**
 * 创建标签
 * @param {* 世界坐标} cartesian
 * @returns
 */
function defaultCreateLabelEntity(cartesian) {
    const posList = CesiumCoord.cartesian3ToLonLatHeight(cartesian, 4); // 世界坐标转化为经纬度坐标
    const entity = new Cesium.Entity({
        position: cartesian, // 世界坐标
        label: {
            text: `经度：${posList[0]},纬度：${posList[1]}`,
            font: '20px',
            color: Cesium.Color.RED,
            pixelOffset: new Cesium.Cartesian2(0, -20),
            fillColor: Cesium.Color.WHITE,
            showBackground: true,
            backgroundColor: Cesium.Color.BLUE,
        },
    });
    return entity;
}
